}
static gboolean
-_check_support_reflink (OstreeRepo *self, gboolean *supported, GError **error)
+_check_support_reflink (OstreeRepo *dest, gboolean *supported, GError **error)
{
- /* We have not checked yet if the file system supports reflinks, do it here */
- if (g_atomic_int_get (&self->fs_support_reflink) == 0)
+ /* We have not checked yet if the destination file system supports reflinks, do it here */
+ if (g_atomic_int_get (&dest->fs_support_reflink) == 0)
{
- g_auto(GLnxTmpfile) src_tmpf = { 0, };
+ glnx_autofd int src_fd = -1;
g_auto(GLnxTmpfile) dest_tmpf = { 0, };
- if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_RDWR|O_CLOEXEC,
- &src_tmpf, error))
+ if (!glnx_openat_rdonly (dest->repo_dir_fd, "config", TRUE, &src_fd, error))
return FALSE;
- if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC,
+ if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (dest), ".", O_WRONLY|O_CLOEXEC,
&dest_tmpf, error))
return FALSE;
- if (ioctl (dest_tmpf.fd, FICLONE, src_tmpf.fd) == 0)
- g_atomic_int_set (&self->fs_support_reflink, 1);
+ if (ioctl (dest_tmpf.fd, FICLONE, src_fd) == 0)
+ g_atomic_int_set (&dest->fs_support_reflink, 1);
else if (errno == EOPNOTSUPP) /* Ignore other kind of errors as they might be temporary failures */
- g_atomic_int_set (&self->fs_support_reflink, -1);
+ g_atomic_int_set (&dest->fs_support_reflink, -1);
}
- *supported = g_atomic_int_get (&self->fs_support_reflink) >= 0;
+ *supported = g_atomic_int_get (&dest->fs_support_reflink) >= 0;
return TRUE;
}
GError **error)
{
gboolean reflinks_supported = FALSE;
+
if (!_check_support_reflink (self, &reflinks_supported, error))
return FALSE;
}
static gboolean
-_import_payload_link (OstreeRepo *self,
- OstreeRepo *source,
+_import_payload_link (OstreeRepo *dest_repo,
+ OstreeRepo *src_repo,
const char *checksum,
GCancellable *cancellable,
GError **error)
glnx_unref_object OtChecksumInstream *checksum_payload = NULL;
g_autoptr(GFileInfo) file_info = NULL;
- if (!_check_support_reflink (self, &reflinks_supported, error))
+ /* The two repositories are on different devices */
+ if (src_repo->device != dest_repo->device)
+ return TRUE;
+
+ if (!_check_support_reflink (dest_repo, &reflinks_supported, error))
return FALSE;
if (!reflinks_supported)
return TRUE;
- if (!G_IN_SET(self->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY))
+ if (!G_IN_SET(dest_repo->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY))
return TRUE;
- if (!ostree_repo_load_file (source, checksum, &is, &file_info, NULL, cancellable, error))
+ if (!ostree_repo_load_file (src_repo, checksum, &is, &file_info, NULL, cancellable, error))
return FALSE;
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_REGULAR
- || g_file_info_get_size (file_info) < self->payload_link_threshold)
+ || g_file_info_get_size (file_info) < dest_repo->payload_link_threshold)
return TRUE;
checksum_payload = ot_checksum_instream_new (is, G_CHECKSUM_SHA256);
}
payload_checksum = ot_checksum_instream_get_string (checksum_payload);
- return _create_payload_link (self, checksum, payload_checksum, file_info, cancellable, error);
+ return _create_payload_link (dest_repo, checksum, payload_checksum, file_info, cancellable, error);
}
static gboolean
_try_clone_from_payload_link (OstreeRepo *self,
+ OstreeRepo *dest_repo,
const char *payload_checksum,
GFileInfo *file_info,
GLnxTmpfile *tmpf,
if (self->commit_stagedir.initialized)
dfd_searches[0] = self->commit_stagedir.fd;
- if (!_check_support_reflink (self, &reflinks_supported, error))
+ /* The two repositories are on different devices */
+ if (self->device != dest_repo->device)
+ return TRUE;
+
+ if (!_check_support_reflink (dest_repo, &reflinks_supported, error))
return FALSE;
if (!reflinks_supported)
return TRUE;
}
}
+ if (self->parent_repo)
+ return _try_clone_from_payload_link (self->parent_repo, dest_repo, payload_checksum, file_info, tmpf, cancellable, error);
return TRUE;
}
/* Check if a file with the same payload is present in the repository,
and in case try to reflink it */
- if (actual_payload_checksum && !_try_clone_from_payload_link (self, actual_payload_checksum, file_info, &tmpf, cancellable, error))
+ if (actual_payload_checksum && !_try_clone_from_payload_link (self, self, actual_payload_checksum, file_info, &tmpf, cancellable, error))
return FALSE;
/* This path is for regular files */
# Use /var/tmp so we have O_TMPFILE etc.
prepare_tmpdir /var/tmp
trap _tmpdir_cleanup EXIT
+# We use this user down below, it needs access too
+setfacl -d -m u:bin:rwX .
+setfacl -m u:bin:rwX .
ostree --repo=repo init --mode=archive
echo -e '[archive]\nzlib-level=1\n' >> repo/config
cleanup() {
cd ${test_tmpdir}
- umount mnt || true
- test -n "${blkdev:-}" && losetup -d ${blkdev} || true
+ for mnt in ${mnts:-}; do
+ umount ${mnt} || true
+ done
+ for blkdev in ${blkdevs:-}; do
+ losetup -d ${blkdev} || true
+ done
}
trap cleanup EXIT
-mkdir mnt
-truncate -s 2G testblk.img
-if ! blkdev=$(losetup --find --show $(pwd)/testblk.img); then
- echo "ok # SKIP not run when cannot setup loop device"
- exit 0
-fi
-
+truncate -s 2G testblk1.img
+blkdev1=$(losetup --find --show $(pwd)/testblk1.img)
+blkdevs="${blkdev1}"
# This filesystem must support reflinks
-mkfs.xfs -m reflink=1 ${blkdev}
+mkfs.xfs -m reflink=1 ${blkdev1}
+mkdir mnt1
+mount ${blkdev1} mnt1
+mnts=mnt1
+
+truncate -s 2G testblk2.img
+blkdev2=$(losetup --find --show $(pwd)/testblk2.img)
+blkdevs="${blkdev1} ${blkdev2}"
+mkfs.xfs -m reflink=1 ${blkdev2}
+mkdir mnt2
+mount ${blkdev2} mnt2
+mnts="mnt1 mnt2"
-mount ${blkdev} mnt
-cd mnt
+cd mnt1
+# See above for setfacl rationale
+setfacl -d -m u:bin:rwX .
+setfacl -m u:bin:rwX .
+ls -al .
+runuser -u bin mkdir foo
+runuser -u bin touch foo/bar
+ls -al foo
# Test that reflink is really there (not just --reflink=auto)
touch a
find repo -type l -name '*.payload-link' >payload-links.txt
assert_streq "$(wc -l < payload-links.txt)" "1"
-# Disable logging for inner loop
-set +x
cat payload-links.txt | while read i; do
payload_checksum=$(basename $(dirname $i))$(basename $i .payload-link)
payload_checksum_calculated=$(sha256sum $(readlink -f $i) | cut -d ' ' -f 1)
assert_streq "${payload_checksum}" "${payload_checksum_calculated}"
done
-set -x
-echo "ok pull creates .payload-link"
+echo "ok payload link"
+
+ostree --repo=repo checkout dupobjects content
+# And another object which differs just in metadata
+cp --reflink=auto content/bigobject{,3}
+chown operator:0 content/bigobject3
+cat >unpriv-child-repo.sh <<EOF
+#!/bin/bash
+# Check that commit to an user repo that has a parent still works
+set -xeuo pipefail
+ostree --repo=child-repo init --mode=bare-user
+ostree --repo=child-repo remote add origin --set=gpg-verify=false ${origin}
+ostree --repo=child-repo config set core.parent $(pwd)/repo
+ostree --repo=child-repo commit -b test content
+EOF
+chmod a+x unpriv-child-repo.sh
+runuser -u bin ./unpriv-child-repo.sh
+find child-repo -type l -name '*.payload-link' >payload-links.txt
+assert_streq "$(wc -l < payload-links.txt)" "0"
+rm content -rf
+
+echo "ok reflink unprivileged with parent repo"
+
+# We can't reflink across devices though
+cd ../mnt2
+ostree --repo=repo init --mode=archive
+ostree --repo=repo config set core.parent $(cd ../mnt1/repo && pwd)
+ostree --repo=../mnt1/repo checkout dupobjects content
+ostree --repo=repo commit -b dupobjects2 --consume --tree=dir=content
+ostree --repo=repo pull --disable-static-deltas origin dupobjects
+find repo -type l -name '*.payload-link' >payload-links.txt
+assert_streq "$(wc -l < payload-links.txt)" "0"
+
+echo "ok payload link across devices"
+
date